home *** CD-ROM | disk | FTP | other *** search
- /*
- * Generic driver for Z8530 SCC chip in SLIP, KISS or AX.25 mode.
- *
- * Written by R.E. Janssen (PE1CHL) using material from earlier
- * EAGLE and PC100 drivers in this package.
- *
- * The driver has initially been written for my own Atari SCC interface
- * board, but it could eventually replace the other SCC drivers.
- *
- * Unfortunately, there is little consistency between the different interface
- * boards, as to the use of a clock source, the solution for the fullduplex
- * clocking problem, and most important of all: the generation of the INTACK
- * signal. Most designs do not even support the generation of an INTACK and
- * the read of the interrupt vector provided by the chip.
- * This results in lots of configuration parameters, and a fuzzy
- * polltable to be able to support multiple chips connected at one interrupt
- * line...
- *
- * when the constant SCC_HWTIMER is defined during compilation of SCC.C,
- * we'll install the timer directly on the timer hardware interrupt, not on the
- * timer-tick user-exit (int 1c) because some programs take over that one
- * without saving and calling the original handler installed there...
- *
- */
-
- #define SCC_HWTIMER /* timer tick on hardware interrupt */
-
- #ifdef ATARI_ST
- # ifdef MWC
- # include <osbind.h>
- # endif
- # ifdef __TURBOC__
- # include <tos.h>
- # endif
- #endif
-
- #include <stdio.h>
- #include <ctype.h>
- #include <time.h>
- #include "global.h"
- #include "mbuf.h"
- #include "buffers.h"
- #include "iface.h"
- #include "slip.h"
- #include "scc.h"
- #include "8530.h"
- #include "8536.h"
- #include "ax25.h"
- #include "trace.h"
-
- /* variables used by the SCC interrupt handler in sccvec.s */
-
- #ifdef ATARI_ST
- ioaddr sccgpiploc; /* location of MFP GPIP register */
- int sccgpipbit; /* bit# to test in MFP GPIP reg */
- ioaddr sccisrloc; /* location of MFP ISR */
- int sccisrbit; /* bit# to clear in MFP ISR */
- extern void (*sccotvec)(); /* location to store old int vec */
-
- struct sccinfo sccinfo; /* global info about SCCs */
- struct sccchan *sccchan[2 * MAXSCC]; /* information per channel */
- ioaddr sccvecloc; /* location to access for SCC vector */
- unsigned char sccmaxvec; /* maximum legal vector from SCC */
- ioaddr sccpolltab[MAXSCC+1][2]; /* polling table when no vectoring */
- #endif
-
- #ifdef MSDOS
- static void (*orgivec)(); /* original interrupt vector */
- extern void (*far sccotvec)(); /* location to store old int vec */
-
- struct sccinfo sccinfo = {0}; /* global info about SCCs */
- struct sccchan *sccchan[2 * MAXSCC] = {0}; /* information per channel */
- ioaddr sccvecloc = {0}; /* location to access for SCC vector */
- unsigned char sccmaxvec = {0}; /* maximum legal vector from SCC */
- ioaddr sccpolltab[MAXSCC+1][2] = {0}; /* polling table when no vectoring */
-
- # if ((defined(MSC) && defined(INLINE)) || (defined(__TURBOC__) && defined(inportb)))
- static unsigned scc_delay (v) /* delay for about 5 PCLK cycles */
- unsigned v; /* pass-through used for input */
-
- {
- return v; /* return the passed parameter */
- }
- # endif
- #endif
-
- unsigned char random = 0; /* random number for p-persist */
-
- extern char nospace[];
-
- static int scc_stop(),scc_raw(),scc_slipraw(),doscc();
- static void scc_async(),scc_sdlc(),scc_recv();
- static void scc_tossb(),scc_txon(),scc_txoff();
- static int scc_ax25(),scc_aioctl(),scc_sioctl();
- static unsigned int scc_speed();
-
- /* Attach an SCC channel to the system, or initialize SCC driver.
- * operation depends on argv[2]:
- * when "init", the SCC driver is initialized, and global information about
- * the hardware is set up.
- * argv[0]: hardware type, must be "scc"
- * argv[1]: number of SCC chips we will support
- * argv[2]: mode, must be: "init" in this case
- * argv[3]: base address of SCC chip #0 (hex)
- * argv[4]: spacing between SCC chip base addresses
- * argv[5]: offset from chip base address to channel A control register
- * argv[6]: offset from chip base address to channel B control register
- * argv[7]: offset from each channel's control register to data register
- * argv[8]: address of INTACK/Read Vector port. 0 to read from RR3A/RR2B
- * argv[9]: CPU interrupt vector number for all connected SCCs
- * argv[10]: clock frequency (PCLK/RTxC) of all SCCs in cycles per second
- * prefix with "p" for PCLK, "r" for RTxC clock (for baudrate gen)
- * argv[11]: optional hardware type (for special features)
- * argv[12]: optional extra parameter for special hardware
- *
- * otherwise, a single channel is attached using the specified parameters:
- * argv[0]: hardware type, must be "scc"
- * argv[1]: SCC channel number to attach, 0/1 for first chip A/B, 2/3 for 2nd...
- * argv[2]: mode, can be:
- * "slip", "kiss", "ax25"
- * argv[3]: interface label, e.g., "sl0"
- * argv[4]: maximum transmission unit, bytes
- * argv[5]: interface speed, e.g, "1200". prefix with "d" when an external
- * divider is available to generate the TX clock. When the clock
- * source is PCLK, this can be a /32 divider between TRxC and RTxC.
- * When the clock is at RTxC, the TX rate must be supplied at TRxC.
- * This is needed only for AX.25 fullduplex.
- * When this arg is given as "ext", the transmit and receive clock
- * are external, and the BRG and DPLL are not used.
- * argv[6]: callsign used on the radio channels
- */
-
- int
- scc_attach(argc,argv)
- int argc;
- char *argv[];
- {
- register struct interface *ifp;
- struct sccchan *scc;
- unsigned int chan,brgrate;
- int pclk = 0;
- char *p;
- char r11;
- int hwtype = 0,hwparam = 0;
- #ifdef ATARI_ST
- long save_ssp;
- #endif
- extern struct interface *ifaces;
- int slip_send(),kiss_ioctl(),kiss_raw();
- void kiss_recv(),slip_recv();
- long atol(),htol();
-
- /* first handle the special "init" mode, to initialize global stuff */
-
- if(!strcmp(argv[2],"init")){
- if(argc < 11) /* need at least argv[1]..argv[10] */
- return -1;
-
- if (isupper(argv[10][0]))
- argv[10][0] = tolower(argv[10][0]);
-
- if (argv[10][0] == 'p'){ /* wants to use PCLK as clock? */
- pclk = 1;
- argv[10]++;
- } else {
- if (argv[10][0] == 'r') /* wants to use RTxC? */
- argv[10]++; /* that's the default */
- }
-
- if (argc > 11) /* optional hardware type */
- hwtype = htoi(argv[11]); /* it is given in hex */
-
- if (argc > 12) /* optional hardware param */
- hwparam = htoi(argv[12]); /* also in hex */
-
- return scc_init(atoi(argv[1]),(ioaddr) htol(argv[3]),atoi(argv[4]),
- atoi(argv[5]),atoi(argv[6]),atoi(argv[7]),
- (ioaddr) htol(argv[8]),atoi(argv[9]),
- atol(argv[10]),pclk,hwtype,hwparam);
- }
-
- /* not "init", so it must be a valid mode to attach a channel */
-
- if (strcmp(argv[2],"ax25") && strcmp(argv[2],"kiss") &&
- strcmp(argv[2],"slip")){
- printf("Mode %s unknown for SCC\n",argv[2]);
- return -1;
- }
-
- if (!sccinfo.init){
- printf("First init SCC driver\n");
- return -1;
- }
-
- if ((chan = atoi(argv[1])) > sccinfo.maxchan){
- printf("SCC channel %d out of range\n",chan);
- return -1;
- }
-
- if (sccchan[chan] != NULLCHAN){
- printf("SCC channel %d already attached\n",chan);
- return -1;
- }
-
- /* create interface structure and fill in details */
-
- if ((ifp = (struct interface *) calloc(1,sizeof(struct interface))) == NULLIF){
- printf(nospace);
- return -1;
- }
-
- if ((ifp->name = malloc(strlen(argv[3]) + 1)) == NULLCHAR){
- printf(nospace);
- free(ifp);
- return -1;
- }
- strcpy(ifp->name,argv[3]);
-
- ifp->mtu = atoi(argv[4]);
- ifp->dev = chan;
- ifp->recv = doscc;
- ifp->stop = scc_stop;
-
- if ((scc = (struct sccchan *) calloc(1,sizeof(struct sccchan))) == NULLCHAN){
- printf(nospace);
- free(ifp->name);
- free(ifp);
- return -1;
- }
-
- scc->ctrl = sccinfo.iobase + (chan / 2) * sccinfo.space + sccinfo.off[chan % 2];
- scc->data = scc->ctrl + sccinfo.doff;
- scc->iface = ifp;
-
- sccchan[chan] = scc; /* put addr in table for interrupts */
-
- switch (argv[2][0]) /* mode already checked above */
- {
- case 'a': /* AX.25 */
- /* default: RXclk DPLL, TXclk BRG. BRG reprogrammed at every TX/RX switch */
- r11 = RCDPLL|TCBR;
-
- /* check for options ('d', 'ext') in the baudrate field */
-
- if (isupper(argv[5][0]))
- argv[5][0] = tolower(argv[5][0]);
-
- switch (argv[5][0])
- {
- case 'd': /* fulldup divider installed? */
- scc->flags |= DIVIDER; /* set appropriate flag */
- argv[5]++; /* skip the 'd' */
-
- if (sccinfo.pclk){ /* when using PCLK as clock source */
- /* RXclk DPLL, TXclk RTxC, out=BRG. external /32 TRxC->RTxC */
- r11 = RCDPLL|TCRTxCP|TRxCOI|TRxCBR;
- } else {
- /* RXclk DPLL, TXclk TRxC. external TX clock to TRxC */
- r11 = RCDPLL|TCTRxCP;
- }
- break;
-
- case 'e': /* external clocking? */
- scc->flags |= EXTCLOCK; /* set the flag */
- r11 = RCRTxCP|TCTRxCP; /* RXclk RTxC, TXclk TRxC. */
- break;
- }
-
- /* option :hexvalue at end of baudrates allows custom R11 setup */
-
- if ((p = strchr(argv[5],':')) != NULLCHAR){
- *p++ = 0;
- r11 = htoi(p);
- }
-
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
-
- scc_sdlc(scc,r11); /* init SCC in SDLC mode */
-
- if (!(scc->flags & EXTCLOCK)) {
- brgrate = scc_speed(scc,32,atol(argv[5]));/* init SCC speed */
- scc->speed = sccinfo.clk / (64L * (brgrate + 2));/* calc real speed */
- }
-
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
-
- /* default KISS Params */
- scc->c.a.params[TXDELAY] = 36*TPS/100; /* 360 ms */
- scc->c.a.params[PERSIST] = 25; /* 10% persistence */
- scc->c.a.params[SLOTTIME] = 16*TPS/100; /* 160 ms */
- scc->c.a.params[TAILTIME] = TPS>67? 3*TPS/100:2; /* 30 ms */
- scc->c.a.params[FULLDUP] = 0; /* CSMA */
- scc->c.a.params[TNCSPECIF] = 1; /* DTR set */
- scc->c.a.params[WAITTIME] = 50*TPS/100; /* 500 ms */
- scc->c.a.params[MAXKEYUP] = 7; /* 7 s */
- scc->c.a.params[MINTIME] = 3; /* 3 s */
- scc->c.a.params[IDLETIME] = 120; /* 120 s */
-
- if (scc_ax25(ifp,argv[2],argv[6])){ /* init AX.25 mode */
- scc_stop(ifp);
- free(ifp->name);
- free(ifp);
- return -1;
- }
-
- ifp->ioctl = scc_sioctl;
- ifp->raw = scc_raw;
- scc->recv = scc_recv;
- break;
-
- case 'k': /* kiss */
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
- scc_async(scc); /* init SCC in async mode */
- brgrate = scc_speed(scc,16,atol(argv[5]));/* init SCC speed */
- scc->speed = sccinfo.clk / (32L * (brgrate + 2));/* calc real speed */
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
-
- if (scc_ax25(ifp,argv[2],argv[6])){ /* init AX.25 mode */
- scc_stop(ifp);
- free(ifp->name);
- free(ifp);
- return -1;
- }
-
- ifp->ioctl = kiss_ioctl;
- ifp->raw = kiss_raw;
- ifp->slipraw = scc_slipraw;
- scc->recv = kiss_recv;
- break;
-
- case 's': /* slip */
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
- scc_async(scc); /* init SCC in async mode */
- brgrate = scc_speed(scc,16,atol(argv[5]));/* init SCC speed */
- scc->speed = sccinfo.clk / (32L * (brgrate + 2));/* calc real speed */
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
-
- ifp->ioctl = scc_aioctl;
- ifp->send = slip_send;
- ifp->output = NULLFP; /* ARP isn't used */
- ifp->raw = ifp->slipraw = scc_slipraw;
- scc->recv = slip_recv;
- break;
- }
-
- ifp->next = ifaces; /* link interface in list */
- ifaces = ifp;
- return 0;
- }
-
- /* SCC driver initialisation. called on "attach scc <num> init ..." */
- /* on the Atari ST, processor must be in supervisor mode */
- static int
- scc_init(nchips,iobase,space,aoff,boff,doff,intack,ivec,clk,pclk,hwtype,hwparam)
- int nchips; /* number of chips */
- ioaddr iobase; /* base of first chip */
- int space,aoff,boff,doff;
- ioaddr intack; /* INTACK ioport or 0 for no INTACK */
- int ivec; /* interrupt vector number */
- long clk; /* clock frequency */
- int pclk; /* PCLK or RTxC for clock */
- int hwtype; /* selection of special hardware types */
- int hwparam; /* extra parameter for special hardware */
-
- {
- int chip,chan;
- ioaddr chipbase;
- register ioaddr ctrl;
- int i_state,d;
- int dum = 1;
- #ifdef ATARI_ST
- int z = 0; /* to avoid clear instruction (68000) */
- long save_ssp; /* saved stackpointer for Super() */
- static char gpipbit[] = {0,1,2,3,8,8,4,5,8,8,8,8,8,8,6,7};
- extern void sccvec(),sccnovec(),scctvec(); /* interrupt handlers */
- #endif
- #ifdef MSDOS
- # define z 0
- # ifdef SCC_HWTIMER
- time_t time1,time2;
- # endif
- extern void sccvec(),sccnovec(),scctvec(); /* interrupt handlers */
- void (*getirq())(); /* Getirq is a function returning a pointer to
- * a function returning void */
- # ifndef __TURBOC__
- void (*getvect())(); /* Getvect too! */
- # endif
- #endif
-
- if (sccinfo.init){
- printf("SCC driver already initialized - nothing done\n");
- return 1;
- }
-
- sccinfo.init = 1;
- sccinfo.nchips = nchips;
- sccinfo.maxchan = (2 * nchips) - 1;
- sccinfo.iobase = iobase;
- sccinfo.space = space;
- sccinfo.off[0] = aoff;
- sccinfo.off[1] = boff;
- sccinfo.doff = doff;
- sccinfo.ivec = ivec;
- sccinfo.clk = clk;
- sccinfo.pclk = pclk;
- sccinfo.hwtype = hwtype;
- sccinfo.hwparam = hwparam;
-
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
-
- /* reset and pre-init all chips in the system */
- for (chip = 0; chip < nchips; chip++){
- chipbase = iobase + chip * space;
- ctrl = chipbase + sccinfo.off[0];
-
- i_state = disable(); /* because of 2-step accesses */
-
- VOID(RDREG(ctrl)); /* make sure pointer is written */
- WRSCC(ctrl,R9,FHWRES); /* force hardware reset */
-
- for (d = 0; d < 1000; d++) /* wait a while to be sure */
- dum *= 10;
-
- for (chan = 0; chan < 2; chan++){
- ctrl = chipbase + sccinfo.off[chan];
-
- /* initialize a single channel to no-op */
-
- VOID(RDREG(ctrl)); /* make sure pointer is written */
- WRSCC(ctrl,R4,z); /* no mode selected yet */
- WRSCC(ctrl,R1,z); /* no W/REQ operation */
- WRSCC(ctrl,R2,16 * chip); /* chip# in upper 4 bits of vector */
- WRSCC(ctrl,R3,z); /* disable rx */
- WRSCC(ctrl,R5,z); /* disable tx */
- WRSCC(ctrl,R9,VIS); /* vector includes status, MIE off */
-
- sccpolltab[chip][chan] = ctrl; /* store ctrl addr for polling */
- }
-
- #ifdef MSDOS
- if (hwtype & EAGLE) /* this is an EAGLE card */
- WRREG(chipbase + 4,0x08); /* enable interrupt on the board */
-
- if (hwtype & PC100) /* this is a PC100 card */
- WRREG(chipbase,hwparam); /* set the MODEM mode (22H normally) */
-
- if (hwtype & PRIMUS) /* this is a PRIMUS-PC */
- WRREG(chipbase + 4,hwparam); /* set the MODEM mode (02H normally) */
-
- if (hwtype & DRSI) /* this is a DRSI PC*Packet card */
- {
- ioaddr z8536 = chipbase + 7; /* point to 8536 master ctrl reg */
-
- /* Initialize 8536 to perform its divide-by-32 function */
- /* This part copied from N6TTO DRSI-driver */
-
- /* Start by forcing chip into known state */
-
- VOID(RDREG(z8536)); /* make sure pointer is written */
- WRSCC(z8536,CIO_MICR,0x01); /* force hardware reset */
-
- for (d = 0; d < 1000; d++) /* wait a while to be sure */
- dum *= 10;
-
- WRSCC(z8536,CIO_MICR,0x00); /* Clear reset and start */
-
- /* Wait for chip to come ready */
-
- while (RDSCC(z8536,CIO_MICR) != 0x02)
- dum *= 10;
-
- WRSCC(z8536,CIO_MICR,0x26); /* NV|CT_VIS|RJA */
- WRSCC(z8536,CIO_MCCR,0xf4); /* PBE|CT1E|CT2E|CT3E|PAE */
-
- WRSCC(z8536,CIO_CTMS1,0xe2);/* Continuous, EOE, ECE, Pulse output */
- WRSCC(z8536,CIO_CTMS2,0xe2);/* Continuous, EOE, ECE, Pulse output */
-
- WRSCC(z8536,CIO_CT1MSB,0x00); /* Load time constant CTC #1 */
- WRSCC(z8536,CIO_CT1LSB,0x10);
- WRSCC(z8536,CIO_CT2MSB,0x00); /* Load time constant CTC #2 */
- WRSCC(z8536,CIO_CT2LSB,0x10);
-
- WRSCC(z8536,CIO_IVR,0x06);
-
- /* Set port direction bits in port A and B */
- /* Data is input on bits d1 and d5, output on d0 and d4. */
- /* The direction is set by 1 for input and 0 for output */
-
- WRSCC(z8536,CIO_PDCA,0x22);
- WRSCC(z8536,CIO_PDCB,0x22);
-
- WRSCC(z8536,CIO_CSR1,CIO_GCB|CIO_TCB); /* Start CTC #1 running */
- WRSCC(z8536,CIO_CSR2,CIO_GCB|CIO_TCB); /* Start CTC #2 running */
- }
- #endif
-
- restore(i_state);
- }
-
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
-
- sccpolltab[chip][0] = 0; /* terminate the polling table */
- sccvecloc = intack; /* location of INTACK/vector read */
- sccmaxvec = 16 * nchips; /* upper limit on valid vector */
-
- #ifdef ATARI_ST
- sccgpiploc = (ioaddr) 0xfffa01L; /* location of GPIP */
- sccgpipbit = gpipbit[ivec]; /* bit number in GPIP */
- sccisrloc = (ioaddr) (ivec > 7? 0xfffa0fL : 0xfffa11L); /* location of ISR */
- sccisrbit = ivec % 8; /* bit number in ISR */
-
- if (intack){ /* INTACK method selected? */
- /* set interrupt vector to INTACK-generating routine */
- Mfpint(ivec,sccvec);
- } else {
- /* set interrupt vector to polling routine */
- Mfpint(ivec,sccnovec);
- }
-
- Jenabint(ivec); /* enable the interrupt */
-
- i_state = disable();
- sccotvec = (void (*)()) Setexc(0x114/4,scctvec); /* install timer interrupt vector */
- restore(i_state);
- #endif
- #ifdef MSDOS
- /* save original interrupt vector */
- orgivec = getirq(ivec);
-
- if (intack){ /* INTACK method selected? */
- /* set interrupt vector to INTACK-generating routine */
- setirq(ivec,sccvec);
- } else {
- /* set interrupt vector to polling routine */
- setirq(ivec,sccnovec);
- }
-
- /* enable the interrupt */
- maskon(ivec);
-
- /* get original timer interrupt vector */
-
- # ifdef SCC_HWTIMER
- sccotvec = getirq(0);
- # else
- sccotvec = getvect(0x1c);
- # endif
-
- /* install timer interrupt handler (that jumps to the original) */
-
- TPS = 18; /* standard ticks/second */
-
- # ifdef SCC_HWTIMER
- setirq(0,scctvec); /* install on hardware int */
-
- # if (defined(MSC) || defined(__TURBOC__))
- /* find the number of ticks per second the hardware timer generates */
-
- time1 = time(NULL);
-
- while ((time2 = time(NULL)) == time1) /* wait for seconds mark */
- ;
-
- sccinfo.ticks = 0; /* clear tally */
- time2 += 4; /* setup a 4-second wait */
-
- while (time(NULL) < time2) /* wait 4 seconds */
- ;
-
- if ((TPS = (sccinfo.ticks + 2) / 4) < 18) /* compute ticks */
- printf("WARNING: SCC timer problem\n");
- else
- printf("SCC: %u ticks/s, %u ms/tick\n",TPS,
- (8000 + sccinfo.ticks) / (2 * sccinfo.ticks));
- # endif
- # else
- setvect(0x1c,scctvec); /* install on software int */
- # endif
- #endif
-
- return 0;
- }
-
- /* initialize an SCC channel in asynchronous mode */
- /* on the Atari ST, processor must be in supervisor mode */
- static void
- scc_async(scc)
- register struct sccchan *scc;
-
- {
- int i_state;
- void scc_asytx(),scc_asyex(),scc_asyrx(),scc_asysp(); /* int handlers */
- #ifdef ATARI_ST
- int z = 0; /* to avoid clear instruction (68000) */
- #endif
-
- scc->int_transmit = scc_asytx; /* set interrupt handlers */
- scc->int_extstat = scc_asyex;
- scc->int_receive = scc_asyrx;
- scc->int_special = scc_asysp;
-
- i_state = disable();
-
- wr(scc,R4,X16CLK|SB1); /* *16 clock, 1 stopbit, no parity */
- wr(scc,R1,z); /* no W/REQ operation */
- wr(scc,R3,Rx8); /* RX 8 bits/char, disabled */
- wr(scc,R5,Tx8|DTR|RTS); /* TX 8 bits/char, disabled, DTR RTS */
- wr(scc,R9,VIS); /* vector includes status */
- wr(scc,R10,NRZ|z); /* select NRZ */
- wr(scc,R11,RCBR|TCBR); /* clocks are BR generator */
- wr(scc,R14,sccinfo.pclk? BRSRC:z); /* brg source = PCLK/RTxC */
- wr(scc,R15,BRKIE); /* enable BREAK ext/status int */
-
- or(scc,R3,RxENABLE); /* enable receiver */
- or(scc,R5,TxENAB); /* enable transmitter */
-
- WRREG(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
- WRREG(scc->ctrl,RES_EXT_INT); /* must be done twice */
- scc->status = RDREG(scc->ctrl); /* read initial status */
-
- or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
- or(scc,R9,MIE); /* master interrupt enable */
-
- restore(i_state);
- }
-
- /* initialize an SCC channel in SDLC mode */
- /* on the Atari ST, processor must be in supervisor mode */
- static void
- scc_sdlc(scc,r11)
- register struct sccchan *scc;
- char r11; /* clockmode to load in WR11 */
-
- {
- int i_state;
- void scc_sdlctx(),scc_sdlcex(),scc_sdlcrx(),scc_sdlcsp(); /* int handlers */
- #ifdef ATARI_ST
- int z = 0; /* to avoid clear instruction (68000) */
- #endif
-
- scc->int_transmit = scc_sdlctx; /* set interrupt handlers */
- scc->int_extstat = scc_sdlcex;
- scc->int_receive = scc_sdlcrx;
- scc->int_special = scc_sdlcsp;
-
- i_state = disable();
-
- wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
- wr(scc,R1,z); /* no W/REQ operation */
- wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
- wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */
- wr(scc,R6,z); /* SDLC address zero (not used) */
- wr(scc,R7,FLAG); /* SDLC flag value */
- wr(scc,R9,VIS); /* vector includes status */
- wr(scc,R10,CRCPS|NRZI|ABUNDER); /* CRC preset 1, select NRZI, ABORT on underrun */
- wr(scc,R11,r11); /* clockmode as determined before */
-
- if (scc->flags & EXTCLOCK){ /* when using external clocks */
- wr(scc,R14,z); /* No BRG options */
- WRSCC(scc->ctrl,R14,DISDPLL|scc->wreg[R14]); /* No DPLL operation */
- } else {
- wr(scc,R14,sccinfo.pclk? BRSRC:z); /* BRG source = PCLK/RTxC */
- WRSCC(scc->ctrl,R14,SSBR|scc->wreg[R14]); /* DPLL source = BRG */
- WRSCC(scc->ctrl,R14,SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
- }
-
- wr(scc,R15,BRKIE|CTSIE|DCDIE); /* enable ABORT, CTS & DCD interrupts */
-
- if (RDREG(scc->ctrl) & DCD){ /* DCD is now ON */
- if (!(scc->flags & EXTCLOCK))
- WRSCC(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
- or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
- }
-
- WRREG(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
- WRREG(scc->ctrl,RES_EXT_INT); /* must be done twice */
- scc->status = RDREG(scc->ctrl); /* read initial status */
-
- or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
- or(scc,R9,MIE); /* master interrupt enable */
-
- restore(i_state);
- }
-
- /* set SCC channel speed */
- /* clkmode specifies the division rate (1,16,32) inside the SCC */
- /* on the Atari ST, processor must be in supervisor mode */
- /* returns the selected brgrate for "real speed" calculation */
-
- static unsigned int scc_speed(scc,clkmode,speed)
- register struct sccchan *scc;
- unsigned int clkmode;
- long speed; /* the desired baudrate */
-
- {
- unsigned int brgrate;
- long spdclkm;
- int i_state;
-
- /* calculate baudrate generator value */
-
- if ((spdclkm = speed * clkmode) == 0)
- return 65000U; /* avoid divide-by-zero */
-
- brgrate = (unsigned) ((sccinfo.clk + spdclkm) / (spdclkm * 2)) - 2;
-
- i_state = disable(); /* 2-step register accesses... */
-
- cl(scc,R14,BRENABL); /* disable baudrate generator */
- wr(scc,R12,brgrate); /* brg rate LOW */
- wr(scc,R13,brgrate >> 8); /* brg rate HIGH */
- or(scc,R14,BRENABL); /* enable baudrate generator */
-
- restore(i_state);
-
- return brgrate;
- }
-
- /* de-activate SCC channel */
- static int
- scc_stop(ifp)
- struct interface *ifp;
-
- {
- struct sccchan *scc = sccchan[ifp->dev];
- int i_state;
- #ifdef ATARI_ST
- long save_ssp;
-
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
-
- i_state = disable();
-
- VOID(RDREG(scc->ctrl)); /* make sure pointer is written */
- wr(scc,R9,(ifp->dev % 2)? CHRB : CHRA); /* reset the channel */
- free(scc);
- sccchan[ifp->dev] = NULLCHAN;
-
- restore(i_state);
-
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
- }
-
- /* de-activate SCC driver on program exit */
- void sccstop()
-
- {
- if (sccinfo.init){ /* was it initialized? */
- #ifdef ATARI_ST
- Jdisint(sccinfo.ivec); /* disable the interrupt */
- Setexc(0x114/4,sccotvec); /* reset the timer vector */
- #endif
- #ifdef MSDOS
- maskoff(sccinfo.ivec); /* disable the interrupt */
- setirq(sccinfo.ivec,orgivec); /* restore original interrupt vector */
- # ifdef SCC_HWTIMER
- setirq(0,sccotvec); /* restore original timer interrupt */
- # else
- setvect(0x1c,sccotvec); /* same, but using INT 1CH */
- # endif
- #endif
- }
- }
-
- /* perform ioctl on SCC (async) channel */
- /* this is used for SLIP mode only, and will read/set the line speed */
- static int
- scc_aioctl(ifp,argc,argv)
- struct interface *ifp;
- int argc;
- char *argv[];
-
- {
- struct sccchan *scc;
- unsigned int brgrate;
- #ifdef ATARI_ST
- long save_ssp;
- #endif
-
- scc = sccchan[ifp->dev];
-
- if (argc < 1){
- printf("%ld\n",scc->speed);
- return 0;
- }
-
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
-
- brgrate = scc_speed(scc,16,atol(argv[0]));
- scc->speed = sccinfo.clk / (32L * (brgrate + 2));
-
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
-
- return 0;
- }
-
- /* perform ioctl on SCC (sdlc) channel */
- /* this is used for AX.25 mode only, and will set the "kiss" parameters */
- static int
- scc_sioctl(ifp,argc,argv)
- struct interface *ifp;
- int argc;
- char *argv[];
-
- {
- struct sccchan *scc;
- int param,i_state;
- unsigned int brgrate;
- char *p;
- #ifdef ATARI_ST
- long save_ssp;
- #endif
-
- scc = sccchan[ifp->dev];
-
- if (argc == 0){
- if (scc->flags & EXTCLOCK)
- printf("ext.clk:%02x",scc->wreg[R11]);
- else
- printf("speed=%s%ld:%02x",(scc->flags & DIVIDER)? "d":"",
- scc->speed,scc->wreg[R11]);
-
- printf(" group=%03x tx=%c",scc->group,(scc->flags & TX_INHIBIT)? 'n':'y');
-
- for (param = 1; param < sizeof(scc->c.a.params); param++)
- printf(" %d=%d",param,scc->c.a.params[param]);
-
- printf("\n");
- return 0;
- }
-
- if (argc < 2){
- printf("Data field missing\n");
- return 1;
- }
-
- switch (argv[0][0]) /* check special param setting */
- {
- case 'g': /* setting of group */
- scc->group = htoi(argv[1]);
- return 0;
-
- case 's': /* setting of speed */
- if (!(scc->flags & EXTCLOCK)) {
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
- /* option :hexvalue at end of baudrates allows custom R11 setup */
-
- if ((p = strchr(argv[1],':')) != NULLCHAR){
- *p++ = 0;
- i_state = disable();
- wr(scc,R11,htoi(p)); /* set new R11 value */
- restore(i_state);
- }
-
- brgrate = scc_speed(scc,32,atol(argv[1]));/* init SCC speed */
- scc->speed = sccinfo.clk / (64L * (brgrate + 2));/* calc real speed */
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
- }
- return 0;
-
- case 't': /* set tx inhibit */
- if (tolower(argv[1][0]) == 'n')
- scc->flags |= TX_INHIBIT; /* no tx = inhibit it */
- else
- scc->flags &= ~TX_INHIBIT;
- return 0;
- }
-
- if ((param = atoi(argv[0])) <= 0 || param >= sizeof(scc->c.a.params)){
- printf("Param number out of range\n");
- return 1;
- }
-
- scc->c.a.params[param] = atoi(argv[1]);
-
- /* and now, a special hack to allow setting of DTR with param #6 */
-
- if (param == TNCSPECIF){
- i_state = disable();
-
- if (scc->c.a.params[TNCSPECIF]) /* set or clear DTR image bit */
- scc->wreg[R5] |= DTR;
- else
- scc->wreg[R5] &= ~DTR;
-
- if (scc->c.a.tstate == IDLE && scc->timercount == 0)
- scc->timercount = 1; /* force an update */
-
- restore(i_state);
- }
-
- return 0;
- }
-
- /* start SCC transmitter when it is idle (SLIP/KISS mode only) */
- static void
- scc_sstart(scc)
- register struct sccchan *scc;
- {
- #ifdef ATARI_ST
- long save_ssp;
- #endif
-
- if (scc->c.s.tbp != NULLBUF || /* busy */
- scc->c.s.sndq == NULLBUF) /* no work */
- return;
-
- scc->c.s.tbp = dequeue(&scc->c.s.sndq);
-
- #ifdef ATARI_ST
- save_ssp = Super(NULL); /* enter supervisor mode */
- #endif
-
- WRREG(scc->data,FR_END); /* write first char to start things */
-
- #ifdef ATARI_ST
- Super(save_ssp); /* back to user mode */
- #endif
- }
-
- /* show SCC status */
- dosccstat()
- {
- register struct sccchan *scc;
- int i;
-
- if (!sccinfo.init){
- printf("SCC driver not initialized\n");
- return 0;
- }
-
- printf("Ch Iface Sent Rcvd Error Space Overr Rxints Txints Exints Spints\n");
-
- for (i = 0; i <= sccinfo.maxchan; i++) {
- if ((scc = sccchan[i]) == NULLCHAN)
- continue;
-
- printf("%2d %-6s %6lu %6lu %7lu %5u %5u %8lu %8lu %8lu %8lu\n",i,scc->iface->name,
- scc->enqueued,scc->rxframes,scc->rxerrs,scc->nospace,scc->rovers,
- scc->rxints,scc->txints,scc->exints,scc->spints);
- }
-
- return 0;
- }
-
- /* process incoming packets. called from main loop to poll rx queue. */
- static int
- doscc(ifp)
- struct interface *ifp;
-
- {
- register struct sccchan *scc;
- int rv = 0;
-
- scc = sccchan[ifp->dev];
-
- while (scc->rqueue != NULLBUF) { /* incoming packets waiting? */
- (*scc->recv)(ifp,dequeue(&scc->rqueue)); /* process first */
- rv++;
- }
-
- #ifdef ATARI_ST
- /* it seems that the MFP sometimes misses the interrupt edge
- * if this happens, no more interrupts occur and the SCC's hang.
- * test if the int input is low, and panic if so.
- */
- if (ifp->dev == 0){
- long save_ssp;
- unsigned char bit;
- int i_state;
- static int intcntr = 0;
- static int trycntr = 0;
- static char mesg1[] = "WARNING: SCC IRQ stuck, recover";
- static char mesg2[] = "PANIC: SCC IRQ stuck, exit";
-
- save_ssp = Super(NULL); /* enter supervisor mode */
- bit = RDREG(sccgpiploc); /* test interrupt level */
- bit |= RDREG(sccgpiploc); /* 2nd time to avoid glitch */
- Super(save_ssp); /* back to user mode */
-
- if (!(bit & (1 << sccgpipbit))){ /* int active now? */
- if (++intcntr > 10) { /* was that true the last 10 times? */
- if (trycntr++ == 0) { /* check for recovery phase */
- log_msg(mesg1);
- printf("%s\n",mesg1);
- save_ssp = Super(NULL); /* enter supervisor mode */
- i_state = disable();
-
- if (sccvecloc) /* INTACK method selected? */
- sccvec(); /* call vectored handler */
- else
- sccnovec(); /* else use non-vectored handler */
-
- restore(i_state);
- Super(save_ssp); /* back to user mode */
- } else { /* already recovering... */
- log_msg(mesg2); /* make it a PANIC */
- printf("%s\n",mesg2);
- doexit(0);
- }
- }
- } else {
- intcntr = trycntr = 0; /* not active, count back to 0 */
- }
- }
- #endif
-
- return rv; /* number of processed packets */
- }
-
- /* process received frame on SCC. used for AX.25 mode only */
- static void
- scc_recv(ifp,bp)
- struct interface *ifp;
- struct mbuf *bp;
- {
- dump(ifp,IF_TRACE_IN,TRACE_AX25,bp);/* trace the incoming packet */
- ax_recv(ifp,bp); /* receive as AX.25 packet */
- }
-
- /* send raw frame to SCC. used for AX.25 */
- static int
- scc_raw(ifp,bp)
- struct interface *ifp;
- struct mbuf *bp;
- {
- struct sccchan *scc;
- int i_state;
-
- dump(ifp,IF_TRACE_OUT,TRACE_AX25,bp);
-
- scc = sccchan[ifp->dev];
-
- if (scc->flags & TX_INHIBIT){ /* transmitter inhibit */
- free_p(bp);
- return -1;
- }
-
- enqueue(&scc->c.a.sndq,bp); /* enqueue packet */
- scc->enqueued++;
-
- i_state = disable();
-
- if (scc->c.a.tstate == IDLE){ /* when transmitter is idle */
- scc->c.a.tstate = DEFER; /* start the key-up sequence */
- scc->c.a.maxdefer = TPS * scc->c.a.params[IDLETIME] /
- scc->c.a.params[SLOTTIME];
- scc->timercount = scc->c.a.params[WAITTIME];
- }
-
- restore(i_state);
- return 0;
- }
-
- /* send raw slip frame to SCC. used for SLIP and KISS */
- static int
- scc_slipraw(ifp,bp)
- struct interface *ifp;
- struct mbuf *bp;
- {
- struct sccchan *scc;
- register struct slip *sp;
-
- scc = sccchan[ifp->dev];
- sp = &scc->c.s; /* address SLIP control structure */
- enqueue(&sp->sndq,bp); /* enqueue packet (unencoded) */
- scc->enqueued++;
-
- if (sp->tbp == NULLBUF)
- scc_sstart(scc);
- }
-
- /* initialize interface for AX.25 use */
- static int
- scc_ax25(ifp,mode,call)
- register struct interface *ifp;
- char *mode;
- char *call;
-
- {
- struct ax25_addr hwaddr;
- struct ax25_call *axc;
- int ax_send(),ax_output();
-
- if (setcall(&hwaddr,call) < 0){
- printf("Interface %s needs a callsign in %s mode\n",ifp->name,mode);
- return -1;
- }
-
- axarp(); /* init AX.25 ARP */
-
- ifp->send = ax_send;
- ifp->output = ax_output;
- ifp->flags |= IF_AX25;
-
- if ((ifp->hwaddr = malloc(sizeof(struct ax25_addr))) == NULLCHAR ||
- (axc = cr_axcall(ax25_call,&hwaddr)) == NULLAXCALL){
- printf(nospace);
- return -1;
- }
-
- memcpy(ifp->hwaddr,&hwaddr,sizeof(struct ax25_addr));
- if (axc->interface != NULLIF)
- axc->flags |= MULTI_IF;
- axc->interface = ifp;
- axc->mode = IP_ARP_CON;
- return 0;
- }
-
-
- /* Interrupt handlers for asynchronous modes (kiss, slip) */
- /* Remember that on the Atari, these routines can ONLY be called */
- /* in SUPERVISOR mode! */
-
- /* Transmitter interrupt handler */
- /* This routine sends data from mbufs in SLIP format */
- static void
- scc_asytx(scc)
- register struct sccchan *scc;
-
- {
- register struct mbuf *bp;
-
- scc->txints++;
-
- if (scc->txchar != 0){ /* a character pending for transmit? */
- WRREG(scc->data,scc->txchar); /* send it now */
- scc->txchar = 0; /* next time, ignore it */
- return;
- }
-
- if (scc->c.s.tbp == NULLBUF){ /* nothing to send? */
- if ((scc->c.s.tbp = scc->c.s.sndq) != NULLBUF){ /* dequeue next frame */
- scc->c.s.sndq = scc->c.s.sndq->anext;
- WRREG(scc->data,FR_END); /* send FR_END to flush line garbage */
- } else {
- WRREG(scc->ctrl,RES_Tx_P); /* else only reset pending int */
- }
- return;
- }
-
- while ((bp = scc->c.s.tbp)->cnt == 0){ /* nothing left in this mbuf? */
- bp = bp->next; /* save link to next */
- scc->c.s.tbp->next = sys_txfree;/* move this mbuf to txfree list */
- sys_txfree = scc->c.s.tbp;
-
- if ((scc->c.s.tbp = bp) == NULLBUF){ /* see if more mbufs follow */
- WRREG(scc->data,FR_END); /* frame complete, send FR_END */
- return;
- }
- }
- /* now bp = scc->c.s.tbp (either from while or from if stmt above) */
-
- switch (uchar(*(bp->data))) /* next data byte to transmit */
- {
- case FR_END:
- scc->txchar = T_FR_END; /* next time, send transposed FR_END */
- WRREG(scc->data,FR_ESC); /* now, send a FR_ESC */
- break;
-
- case FR_ESC:
- scc->txchar = T_FR_ESC; /* next time, send transposed FR_ESC */
-
- default:
- WRREG(scc->data,*(bp->data)); /* just send the character */
- break;
- }
-
- bp->cnt--; /* decrease mbuf byte count */
- bp->data++; /* and increment the data pointer */
- }
-
- /* External/Status interrupt handler */
- static void
- scc_asyex(scc)
- register struct sccchan *scc;
-
- {
- register unsigned char status,changes;
-
- scc->exints++;
- status = RDREG(scc->ctrl);
- changes = status ^ scc->status;
-
- if (changes & BRK_ABRT){ /* BREAK? */
- scc->c.s.errors++; /* treat as an error to discard frame */
-
- if ((status & BRK_ABRT) == 0) /* BREAK now over? */
- VOID(RDREG(scc->data)); /* read the NUL character */
- }
-
- scc->status = status;
- WRREG(scc->ctrl,RES_EXT_INT);
- }
-
- /* Receiver interrupt handler */
- static void
- scc_asyrx(scc)
- register struct sccchan *scc;
-
- {
- register unsigned char ch;
- register struct mbuf *bp;
-
- scc->rxints++;
-
- if (scc->c.s.escaped){
- /* Translate 2-char escape sequence back to original char */
- scc->c.s.escaped = 0;
-
- switch (ch = RDREG(scc->data))
- {
- case T_FR_ESC:
- ch = FR_ESC;
- break;
-
- case T_FR_END:
- ch = FR_END;
- break;
-
- case FR_END: /* escaped FR_END, assume an error */
- goto discard;
-
- default:
- scc->c.s.errors++;
- break;
- }
- } else {
- switch (ch = RDREG(scc->data))
- {
- case FR_ESC:
- scc->c.s.escaped = 1;
- return;
-
- case FR_END:
- if (scc->c.s.rbp != NULLBUF){
- if (scc->c.s.errors == 0){
- if (scc->rqueue == NULLBUF){ /* enqueue it */
- scc->rqueue = scc->c.s.rbp;
- } else {
- for (bp = scc->rqueue; bp->anext != NULLBUF; bp = bp->anext)
- ;
- bp->anext = scc->c.s.rbp;
- } /* like enqueue() does */
-
- scc->c.s.rbp = scc->c.s.rbp1 = NULLBUF;
- scc->rxframes++;
- } else {
- discard: /* error in frame, put buffers back on sys_rxbufs queue */
- while ((bp = scc->c.s.rbp) != NULLBUF){
- scc->c.s.rbp = bp->next;
- bp->next = sys_rxbufs;
- sys_rxbufs = bp;
- bp->cnt = 0; /* reset count for next user */
- cur_rxbufs++;
- }
-
- scc->c.s.rbp1 = NULLBUF;
- scc->rxerrs++;
- }
- }
- scc->c.s.errors = 0;
- return;
-
- default: /* a normal char, fall through */
- break;
- }
- }
- /* now ch holds the next character for the receive buffer */
-
- if (scc->c.s.errors) /* we already have an error in this frame */
- return; /* so don't waste time and buffers */
-
- if ((bp = scc->c.s.rbp1) == NULLBUF){ /* no buffer available now */
- if ((bp = sys_rxbufs) == NULLBUF){ /* system out of buffers */
- if (!scc->c.s.errors)
- scc->nospace++; /* count these events (1 per frame) */
- scc->c.s.errors++; /* cannot store the character now */
- return;
- }
-
- sys_rxbufs = bp->next; /* get the buffer from list */
- bp->next = NULLBUF;
- cur_rxbufs--;
-
- if (scc->c.s.rbp == NULLBUF){ /* append it to rx list */
- scc->c.s.rbp = scc->c.s.rbp1 = bp;
- } else {
- scc->c.s.rbp1 = bp;
- for (bp = scc->c.s.rbp; bp->next != NULLBUF; bp = bp->next)
- ;
- bp->next = scc->c.s.rbp1;
- bp = scc->c.s.rbp1;
- } /* like append() does */
- }
- /* now bp = scc->c.s.rbp1 */
-
- bp->data[bp->cnt++] = ch; /* store the character */
-
- if (bp->cnt == bp->size) /* buffer full? */
- scc->c.s.rbp1 = NULLBUF; /* acquire a new one next time */
- }
-
- /* Receive Special Condition interrupt handler */
- static void
- scc_asysp(scc)
- register struct sccchan *scc;
-
- {
- register unsigned char status;
-
- scc->spints++;
-
- status = rd(scc,R1); /* read receiver status */
- VOID(RDREG(scc->data)); /* flush offending character */
-
- if (status & CRC_ERR){ /* did a framing error occur? */
- scc->c.s.errors++; /* treat as an error to discard frame */
- } else {
- if (status & Rx_OVR){ /* did we get an overrun? */
- scc->rovers++; /* count them */
- scc->c.s.errors++; /* treat as an error to discard frame */
- }
- }
-
- WRREG(scc->ctrl,ERR_RES);
- }
-
-
- /* Interrupt handlers for sdlc mode (AX.25) */
- /* Remember that on the Atari, these routines can ONLY be called */
- /* in SUPERVISOR mode! */
-
- /* Transmitter interrupt handler */
- static void
- scc_sdlctx(scc)
- register struct sccchan *scc;
-
- {
- register struct mbuf *bp;
-
- scc->txints++;
-
- switch (scc->c.a.tstate) /* look at transmitter state */
- {
- case ACTIVE: /* busy sending data bytes */
- while ((bp = scc->c.a.tbp)->cnt == 0){ /* nothing left in this mbuf? */
- bp = bp->next; /* save link to next */
- scc->c.a.tbp->next = sys_txfree; /* move this mbuf to txfree list */
- sys_txfree = scc->c.a.tbp;
-
- if ((scc->c.a.tbp = bp) == NULLBUF){/* see if more mbufs follow */
- if (RDREG(scc->ctrl) & TxEOM){ /* check tx underrun status */
- scc->rovers++; /* oops, an underrun! count them */
- WRREG(scc->ctrl,SEND_ABORT);/* send an abort to be sure */
- scc->c.a.tstate = TAIL; /* key down tx after TAILTIME */
- scc->timercount = scc->c.a.params[TAILTIME];
- return;
- }
-
- cl(scc,R10,ABUNDER); /* frame complete, allow CRC transmit */
- scc->c.a.tstate = FLUSH;
- WRREG(scc->ctrl,RES_Tx_P); /* reset pending int */
- return;
- }
- }
- /* now bp = scc->c.a.tbp (either from while or from if stmt above) */
-
- WRREG(scc->data,*(bp->data++)); /* send the character */
- bp->cnt--; /* decrease mbuf byte count */
- return;
-
- case FLUSH: /* CRC just went out, more to send? */
- or(scc,R10,ABUNDER); /* re-install underrun protection */
- /* verify that we are not exeeding max tx time (if defined) */
- if ((scc->timercount != 0 || scc->c.a.params[MAXKEYUP] == 0) &&
- (scc->c.a.tbp = scc->c.a.sndq) != NULLBUF){ /* dequeue a frame */
- scc->c.a.sndq = scc->c.a.sndq->anext;
- WRREG(scc->ctrl,RES_Tx_CRC); /* reset the TX CRC generator */
- scc->c.a.tstate = ACTIVE;
- scc_sdlctx(scc); /* write 1st byte */
- WRREG(scc->ctrl,RES_EOM_L); /* reset the EOM latch */
- return;
- }
-
- scc->c.a.tstate = TAIL; /* no more, key down tx after TAILTIME */
- scc->timercount = scc->c.a.params[TAILTIME];
- WRREG(scc->ctrl,RES_Tx_P);
- return;
-
- default: /* another state */
- WRREG(scc->ctrl,RES_Tx_P); /* then don't send anything */
- return;
- }
- }
-
- /* External/Status interrupt handler */
- static void
- scc_sdlcex(scc)
- register struct sccchan *scc;
-
- {
- register unsigned char status,changes;
-
- scc->exints++;
- status = RDREG(scc->ctrl);
- changes = status ^ scc->status;
-
- if (changes & BRK_ABRT){ /* Received an ABORT */
- if (status & BRK_ABRT){ /* is this the beginning? */
- if (scc->c.a.rbp != NULLBUF){/* did we receive something? */
- /* check if a significant amount of data came in */
- /* this is because the drop of DCD tends to generate an ABORT */
- if (scc->c.a.rbp->next != NULLBUF || scc->c.a.rbp->cnt > 10)
- scc->rxerrs++; /* then count it as an error */
- scc_tossb(scc); /* throw away buffer */
- }
- VOID(RDREG(scc->data)); /* flush the FIFO */
- VOID(RDREG(scc->data));
- VOID(RDREG(scc->data));
- }
- }
-
- if (changes & CTS){ /* CTS input changed state */
- if (status & CTS){ /* CTS is now ON */
- if (scc->c.a.tstate == KEYWT &&
- scc->c.a.params[TXDELAY] == 0) /* zero TXDELAY = wait for CTS */
- scc->timercount = 1; /* it will start within 10 ms */
- }
- }
-
- if (changes & DCD){ /* DCD input changed state */
- if (status & DCD){ /* DCD is now ON */
- if (!(scc->flags & EXTCLOCK))
- WRSCC(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
- or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
- } else { /* DCD is now OFF */
- cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
- VOID(RDREG(scc->data)); /* flush the FIFO */
- VOID(RDREG(scc->data));
- VOID(RDREG(scc->data));
- if (scc->c.a.rbp != NULLBUF){/* did we receive something? */
- /* check if a significant amount of data came in */
- /* this is because some characters precede the drop of DCD */
- if (scc->c.a.rbp->next != NULLBUF || scc->c.a.rbp->cnt > 10)
- scc->rxerrs++; /* then count it as an error */
- scc_tossb(scc); /* throw away buffer */
- }
- }
- }
-
- scc->status = status;
- WRREG(scc->ctrl,RES_EXT_INT);
- }
-
- /* Receiver interrupt handler */
- static void
- scc_sdlcrx(scc)
- register struct sccchan *scc;
-
- {
- register struct mbuf *bp;
-
- scc->rxints++;
-
- if ((bp = scc->c.a.rbp1) == NULLBUF){ /* no buffer available now */
- if ((bp = sys_rxbufs) == NULLBUF){ /* system out of buffers */
- VOID(RDREG(scc->data)); /* so we have to discard the char */
- or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
- scc_tossb(scc); /* put buffers back on pool */
- scc->nospace++; /* count these events */
- return;
- }
-
- sys_rxbufs = bp->next; /* get the buffer from list */
- bp->next = NULLBUF;
- cur_rxbufs--;
-
- if (scc->c.a.rbp == NULLBUF){ /* append it to rx list */
- scc->c.a.rbp = scc->c.a.rbp1 = bp;
- } else {
- scc->c.a.rbp1 = bp;
- for (bp = scc->c.a.rbp; bp->next != NULLBUF; bp = bp->next)
- ;
- bp->next = scc->c.a.rbp1;
- bp = scc->c.a.rbp1;
- } /* like append() does */
- }
-
- /* now, we have a buffer (at bp). read character and store it */
-
- bp->data[bp->cnt++] = RDREG(scc->data);
-
- if (bp->cnt == bp->size) /* buffer full? */
- scc->c.a.rbp1 = NULLBUF; /* acquire a new one next time */
- }
-
- /* Receive Special Condition interrupt handler */
- static void
- scc_sdlcsp(scc)
- register struct sccchan *scc;
-
- {
- register unsigned char status;
- register struct mbuf *bp;
-
- scc->spints++;
-
- status = rd(scc,R1); /* read receiver status */
- VOID(RDREG(scc->data)); /* flush offending character */
-
- if (status & Rx_OVR){ /* receiver overrun */
- scc->rovers++; /* count them */
- or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
- scc_tossb(scc); /* put buffers back on pool */
- }
-
- if (status & END_FR && /* end of frame */
- scc->c.a.rbp != NULLBUF){ /* at least received something */
- if ((status & CRC_ERR) == 0 && /* no CRC error is indicated */
- (status & 0xe) == RES8){ /* 8 bits in last byte */
-
- /* we seem to have a good frame. but the last byte received */
- /* from rx interrupt is in fact a CRC byte, so discard it */
- if (scc->c.a.rbp1 != NULLBUF){
- scc->c.a.rbp1->cnt--; /* current mbuf was not full */
- } else {
- for (bp = scc->c.a.rbp; bp->next != NULLBUF; bp = bp->next)
- ; /* find last mbuf */
-
- bp->cnt--; /* last byte is first CRC byte */
- }
-
- if (scc->rqueue == NULLBUF){/* enqueue it */
- scc->rqueue = scc->c.a.rbp;
- } else {
- for (bp = scc->rqueue; bp->anext != NULLBUF; bp = bp->anext)
- ;
- bp->anext = scc->c.a.rbp;
- } /* like enqueue() does */
-
- scc->c.a.rbp = scc->c.a.rbp1 = NULLBUF;
- scc->rxframes++;
- } else { /* a bad frame */
- scc_tossb(scc); /* throw away frame */
- scc->rxerrs++;
- }
- }
-
- WRREG(scc->ctrl,ERR_RES);
- }
-
- /* Throw away receive mbuf(s) when an error occurred */
- static void
- scc_tossb (scc)
- register struct sccchan *scc;
-
- {
- register struct mbuf *bp;
-
- if (scc->c.a.rbp != NULLBUF){
- /* move all buffers back to the sys_rxbufs list */
- while ((bp = scc->c.a.rbp) != NULLBUF){
- scc->c.a.rbp = bp->next;
- bp->next = sys_rxbufs;
- sys_rxbufs = bp;
- bp->cnt = 0; /* reset count for next user */
- cur_rxbufs++;
- }
-
- scc->c.a.rbp1 = NULLBUF;
- }
- }
-
- /* Switch the SCC to "transmit" mode */
- /* Only to be called from an interrupt handler, while in AX.25 mode */
- static void
- scc_txon(scc)
- register struct sccchan *scc;
- {
- if (!(scc->flags & (DIVIDER|EXTCLOCK))){ /* halfduplex without divider? */
- cl(scc,R3,RxENABLE); /* then switch off receiver */
- cl(scc,R5,TxENAB); /* transmitter off during switch */
- scc_speed(scc,1,scc->speed); /* reprogram baudrate generator */
- }
-
- or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
-
- #ifdef MSDOS
- if (sccinfo.hwtype & PRIMUS) /* PRIMUS has another PTT bit... */
- WRREG(scc->ctrl + 4,sccinfo.hwparam | 0x80); /* set that bit! */
- #endif
- }
-
- /* Switch the SCC to "receive" mode (or: switch off transmitter) */
- /* Only to be called from an interrupt handler, while in AX.25 mode */
- static void
- scc_txoff(scc)
- register struct sccchan *scc;
- {
- cl(scc,R5,RTS); /* turn off RTS line */
-
- #ifdef MSDOS
- if (sccinfo.hwtype & PRIMUS) /* PRIMUS has another PTT bit... */
- WRREG(scc->ctrl + 4,sccinfo.hwparam); /* clear that bit! */
- #endif
-
- if (!(scc->flags & (DIVIDER|EXTCLOCK))){ /* halfduplex without divider? */
- cl(scc,R5,TxENAB); /* then disable the transmitter */
- scc_speed(scc,32,scc->speed); /* back to receiver baudrate */
- }
- }
-
- /* SCC timer interrupt handler. Will be called every 1/TPS s by the system */
- /* timer interrupt. The usual timer library is not used because it will */
- /* only fire handlers from the main program loop. This is much too slow. */
-
- void scctim()
-
- {
- register struct sccchan *scc;
- register struct sccchan **sccp;
-
- #ifdef MSDOS
- sccinfo.ticks++; /* keep track of number of ticks */
- #endif
-
- for (sccp = sccchan + sccinfo.maxchan; sccp >= sccchan; sccp--){
- if ((scc = *sccp) != NULLCHAN &&
- scc->timercount != 0 &&
- --(scc->timercount) == 0){
- /* handle an SCC timer event for this SCC channel */
- /* this can only happen when the channel is AX.25 type */
- /* (the SLIP/KISS driver does not use timers) */
-
- switch (scc->c.a.tstate)
- {
- case IDLE: /* it was idle, this is FULLDUP2 timeout */
- scc_txoff(scc); /* switch-off the transmitter */
- break;
-
- case DEFER: /* trying to get the channel */
- /* operation is as follows:
- * CSMA: when channel clear AND persistence randomgenerator
- * wins, AND group restrictions allow it:
- * keyup the transmitter
- * if not, delay one SLOTTIME and try again
- * FULL: always keyup the transmitter
- */
- if (scc->c.a.params[FULLDUP] == 0){
- random = 21 * random + 53;
-
- if (scc->status & DCD || scc->c.a.params[PERSIST] < random){
- /* defer transmission again. check for limit */
- defer_it: if (--(scc->c.a.maxdefer) == 0){
- /* deferred too long. choice is to:
- * - throw away pending frames, or
- * - smash-on the transmitter and send them.
- * the first would be the choice in a clean
- * environment, but in the amateur radio world
- * a distant faulty station could tie us up
- * forever, so the second may be better...
- */
- #ifdef THROW_AWAY_AFTER_DEFER_TIMEOUT
- struct mbuf *bp,*bp1;
-
- while ((bp = scc->c.a.sndq) != NULLBUF){
- scc->c.a.sndq = scc->c.a.sndq->anext;
- /* move this mbuf chain to the txfree list */
- for (bp1 = bp; bp1->next != NULLBUF; bp1 = bp1->next)
- ;
- bp1->next = sys_txfree;
- sys_txfree = bp;
- }
- #else
- goto keyup; /* just keyup the transmitter... */
- #endif
- }
-
- scc->timercount = scc->c.a.params[SLOTTIME];
- break;
- }
-
- if (uchar(scc->group) != NOGROUP)
- {
- int i;
- struct sccchan *scc2;
-
- for (i = 0; i <= sccinfo.maxchan; i++)
- if ((scc2 = sccchan[i]) != NULLCHAN &&
- scc2 != scc &&
- uchar(scc2->group) & uchar(scc->group) &&
- ((scc->group & TXGROUP && scc2->wreg[R5] & RTS) ||
- (scc->group & RXGROUP && scc2->status & DCD))){
- goto defer_it;
- }
- }
- }
-
- case KEYUP: /* keyup transmitter (note fallthrough) */
- keyup: if ((scc->wreg[R5] & RTS) == 0){ /* when not yet keyed */
- scc->c.a.tstate = KEYWT;
- scc->timercount = scc->c.a.params[TXDELAY]; /* 0 if CTSwait */
- scc_txon(scc);
- break;
- }
- /* when already keyed, directly fall through */
-
- case KEYWT: /* waited for CTS or TXDELAY */
- /* when a frame is available (it should be...):
- * - dequeue it from the send queue
- * - reset the transmitter CRC generator
- * - set a timeout on transmission length, if defined
- * - send the first byte of the frame
- * - reset the EOM latch
- * when no frame available, proceed to TAIL handling
- */
- if ((scc->c.a.tbp = scc->c.a.sndq) != NULLBUF){
- scc->c.a.sndq = scc->c.a.sndq->anext;
- WRREG(scc->ctrl,RES_Tx_CRC);
- scc->c.a.tstate = ACTIVE;
- scc->timercount = TPS * scc->c.a.params[MAXKEYUP];
- scc_sdlctx(scc);
- WRREG(scc->ctrl,RES_EOM_L);
- break;
- }
- /* when no frame queued, fall through to TAIL case */
-
- case TAIL: /* at end of frame */
- /* when fulldup is 0 or 1, switch off the transmitter.
- * when frames are still queued (because of transmit time limit),
- * restart the procedure to get the channel after MINTIME.
- * when fulldup is 2, the transmitter remains keyed and we
- * continue sending. IDLETIME is an idle timeout in this case.
- */
- if (scc->c.a.params[FULLDUP] < 2){
- scc->c.a.tstate = IDLE;
- scc_txoff(scc);
-
- if (scc->c.a.sndq != NULLBUF){
- scc->c.a.tstate = DEFER;
- scc->c.a.maxdefer = TPS * scc->c.a.params[IDLETIME] /
- scc->c.a.params[SLOTTIME];
- scc->timercount = TPS * scc->c.a.params[MINTIME];
- }
- break;
- }
-
- if (scc->c.a.sndq != NULLBUF){ /* still frames on the queue? */
- scc->c.a.tstate = KEYWT; /* continue sending */
- scc->timercount = TPS * scc->c.a.params[MINTIME]; /* after mintime */
- } else {
- scc->c.a.tstate = IDLE;
- scc->timercount = TPS * scc->c.a.params[IDLETIME];
- }
- break;
-
- case ACTIVE: /* max keyup time expired */
- case FLUSH: /* same while in flush mode */
- break; /* no action required yet */
-
- default: /* unexpected state */
- scc->c.a.tstate = IDLE; /* that should not happen, but... */
- scc_txoff(scc); /* at least stop the transmitter */
- break;
- }
- }
- }
- }
-